package com.gylang.print.richtext.processor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.gylang.print.pdf.util.PdfUtil;
import com.gylang.print.richtext.Coords;
import com.gylang.print.richtext.RichHelper;
import com.gylang.print.richtext.TagPattern;
import com.gylang.print.richtext.pdf.RichPdfRender;
import com.gylang.print.richtext.component.RichBox;
import com.gylang.print.richtext.component.BaseRich;
import com.gylang.print.richtext.append.IRichAppend;
import com.gylang.print.richtext.resolver.StyleResolver;
import com.gylang.print.richtext.component.BaseRichText;
import com.gylang.print.util.JsoupUtil;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.awt.*;
import java.io.IOException;
import java.util.*;
import java.util.List;

/**
 * 文本占位替换
 *
 * @author gylang
 * data 2021/7/8
 */
public class TextProcessor implements IRichAppend, StyleResolver, RichPdfRender<BaseRichText> {


	private static final Set<String> TEXT_TAG = new HashSet<>();

	static {
		TEXT_TAG.add("b");
		TEXT_TAG.add("font");
		TEXT_TAG.add("i");
		TEXT_TAG.add("u");
		TEXT_TAG.add("strike");
		TEXT_TAG.add("p");
		TEXT_TAG.add("blockquote");

	}

	@Override
	public Element append(Element element, Map<String, Object> data) {

		// 获取占位符
		String tagName = element.tagName();
		if (isTextTag(tagName)) {
			replaceAll(data, element);
		}
		return element;
	}

	private boolean isTextTag(String tagName) {

		return TEXT_TAG.contains(tagName) || ReUtil.isMatch("h\\d", tagName);
	}


	@Override
	public boolean resolve(Element element, List<BaseRich> component, BaseRich parent) {

		String tagName = element.tagName();
		String outerHtml = element.outerHtml();
		if (isTextTag(tagName) || StrUtil.isNotEmpty(element.ownText())) {
			BaseRichText richText = new BaseRichText();
			richText.copyStyle(parent);
			// 解析样式
			if (TagPattern.B.getRegex().matcher(tagName).matches()) {
				richText.setIfNoNull(BaseRich.BOLD, true);
			}
			if (TagPattern.FONT.getRegex().matcher(tagName).matches()) {
				richText.setIfNoEmpty(BaseRich.FONT, JsoupUtil.findFirstAttr("face", element));
			}
			String size = JsoupUtil.findFirstAttr("size", element);
			if (StrUtil.isNotEmpty(size)) {
				richText.setIfNoNull(BaseRich.SIZE, Float.parseFloat(size));
			}
			if (TagPattern.U.getRegex().matcher(tagName).matches()) {
				richText.setIfNoNull(BaseRich.UNDER_LINE, true);
			}
			if (TagPattern.STRIKE.getRegex().matcher(tagName).matches()) {
				richText.setIfNoNull(BaseRich.DELETE_LINE, true);
			}

			String lineHeight = CollUtil.getFirst(ReUtil.findAllGroup1("line-height:(\\w+)", outerHtml));
			richText.setIfNoNull(BaseRich.SIZE, Integer.getInteger(element.attr("size"), 0xFFFF));
			if (StrUtil.isNotEmpty(lineHeight)) {
				richText.setIfNoNull(BaseRich.LINE_HIGH, Float.parseFloat(lineHeight));

			}
			String retract = CollUtil.getFirst(ReUtil.findAllGroup1("padding-left:(\\w+)em", outerHtml));
			if (StrUtil.isNotEmpty(retract)) {
				richText.setIfNoNull(BaseRich.RETRACT, Float.parseFloat(retract));

			}
			List<String> bgColor = ReUtil.findAllGroup1("background-color: rgb\\(([\\d,\\s]+)\\)", outerHtml);
			String first = CollUtil.getFirst(bgColor);
			if (StrUtil.isNotEmpty(first)) {
				int[] rgb = StrUtil.splitToInt(first, ",");
				if (rgb.length == 3) {
					int rgbInt = Color.getHSBColor(rgb[0], rgb[1], rgb[2]).getRGB();
					richText.setIfNoNull(BaseRich.CODE, rgbInt);
				}
			}
			String align = CollUtil.getFirst(ReUtil.findAllGroup1("text-align:(\\w+)", outerHtml));
			if (StrUtil.isNotEmpty(align)) {
				richText.setIfNoNull(BaseRich.ALIGN, BaseRich.Align.getByName(align));

			}
			richText.setText(element.ownText());
			component.add(richText);
			// 递归 嵌套子级样式
			Elements children = element.children();
			if (CollUtil.isNotEmpty(children)) {
				List<BaseRich> meChild = new ArrayList<>();
				for (Element child : children) {
					List<BaseRich> childList = new ArrayList<>();
					RichHelper.style(child, childList, richText);
					BaseRichText childText = new BaseRichText();
					childText.copyStyle(richText);
					childText.setChild(childList);
					meChild.add(childText);
				}
				richText.setChild(meChild);
			}
			return true;
		}
		return false;
	}

	@Override
	public Coords render(Coords lastNodeCoords, RichBox box, BaseRichText richText) {
		PDPageContentStream stream = box.getContentStream();
		float x = 0;
		float y = 0;
		try {


			//切割行

			List<List<BaseRich>> multiLine = splitLines(richText, box, lastNodeCoords);

			// 解析坐标
			resolveCoords(multiLine);
			// 设置box
			float cx = lastNodeCoords.ex();
			float cy = lastNodeCoords.ey();
			for (List<BaseRich> baseRiches : multiLine) {

				// 渲染每一行
				// 如果是文本直接渲染 非文本 通过组件渲染
				cx = x(baseRiches, box.getWidth(), richText.get(BaseRich.ALIGN));
				cy -= y(baseRiches);
				for (BaseRich baseRich : baseRiches) {

					if (baseRich instanceof BaseRichText) {
						y = cy;
						BaseRichText text = (BaseRichText) baseRich;
						if (StrUtil.isEmpty(text.getText())) {
							continue;
						}
						stream.beginText();
						// 渲染文本
						// 设置文本粗体
						PDFont font = box.getFont(richText.get(BaseRich.FONT));
						float fontSize = richText.getNullDefault(BaseRich.FONT_SIZE, box.getFontSize());
						richText.<Boolean>isPrecent(BaseRich.BOLD, bold -> font.getFontDescriptor().setForceBold(bold));
						// 设置文本斜体
						richText.<Boolean>isPrecent(BaseRich.ITALIC, italic -> font.getFontDescriptor().setItalic(italic));
						// 设置文本删除先
						richText.<Boolean>isPrecent(BaseRich.DELETE_LINE, delete -> font.getFontDescriptor().setNonSymbolic(delete));
						// 设置下划线
						richText.<Boolean>isPrecent(BaseRich.UNDER_LINE, under -> font.getFontDescriptor().setSerif(under));
						// 设置文本定位

						stream.setFont(font, fontSize);
						stream.newLineAtOffset(cx, cy);
						stream.showText(text.getText());
						stream.endText();
					} else {
						RichHelper.renderPdf(baseRich, Coords.of(cx, cy), box);
					}
					cx += baseRich.width();
					x = cx;
				}
			}
//
		} catch (IOException e) {
			e.printStackTrace();
		}
		return Coords.of(x, y);
	}


	private void resolveCoords(List<List<BaseRich>> multiLine) {

	}

	private List<List<BaseRich>> splitLines(BaseRichText richText, RichBox box, Coords lastNodeCoords) {

		List<BaseRich> baseRiches = new ArrayList<>();
		split(richText, baseRiches, box);
		List<List<BaseRich>> multiLine = new ArrayList<>();
		if (CollUtil.isNotEmpty(baseRiches)) {

			// 按对齐方式进行分组 分段
			BaseRich.Align align = baseRiches.get(0).getNullDefault(BaseRich.ALIGN, BaseRich.Align.LEFT);
			if (lastNodeCoords.ex() > 0 && !BaseRich.Align.LEFT.equals(align)) {
				lastNodeCoords.setX(0);
				lastNodeCoords.setY(lastNodeCoords.getY() * lastNodeCoords.getHeight() * box.getFontSize());
			}
			List<BaseRich> line = new ArrayList<>();
			multiLine.add(line);
			float maxWidth = box.getWidth();
			for (BaseRich baseRich : baseRiches) {
				// 字体对齐改变
				if (!align.equals(baseRich.getNullDefault(BaseRich.ALIGN, BaseRich.Align.LEFT))) {
					align = baseRich.getNullDefault(BaseRich.ALIGN, BaseRich.Align.LEFT);
					line = new ArrayList<>();
					multiLine.add(line);
					maxWidth = box.getWidth();
				}
				float width = baseRich.width();
				if (maxWidth - width < 0) {
					// 判断是否进行切割 或者换行
					if (baseRich.notFillBr()) {
						// 直接整体换行
						line = new ArrayList<>();
						line.add(baseRich);
						multiLine.add(line);
						line = new ArrayList<>();
						multiLine.add(line);
						maxWidth = box.getWidth();
					} else {
						// 切割
						maxWidth = box.getWidth() - splitIndex(baseRich, box, maxWidth, multiLine);
					}
				} else {
					maxWidth -= width;
					line.add(baseRich);
				}
			}
		}


		return multiLine;
	}

	private float splitIndex(BaseRich baseRich, RichBox box, float maxWidth, List<List<BaseRich>> multiLine) {

		if (baseRich instanceof BaseRichText) {
			BaseRichText richText = (BaseRichText) baseRich;
			List<String> lines = PdfUtil.splitLines(richText.getText(), richText.getNullDefault(BaseRich.FONT_SIZE, box.getFontSize()), box.getFont(richText.get(BaseRich.FONT)), maxWidth);
			for (String s : lines) {
				BaseRichText text = new BaseRichText(box);
				text.setText(s);
				text.copyStyle(baseRich);
				multiLine.add(CollUtil.newArrayList(text));
			}
			return PdfUtil.getTextLength(box.getFont(baseRich.get(BaseRich.FONT)), baseRich.getNullDefault(BaseRich.FONT_SIZE, box.getFontSize()), CollUtil.getLast(lines));
		}
		return 0;
	}

	private void split(BaseRich richText, List<BaseRich> list, RichBox box) {

		if (null == richText) {
			return;
		}
		richText.setBox(box);
		list.add(richText);
		if (CollUtil.isNotEmpty(richText.getChild())) {
			for (BaseRich baseRich : richText.getChild()) {
				split(baseRich, list, box);
			}
		}
	}

	float x(List<BaseRich> contentList, float width, BaseRich.Align align) {

		float actualWidth = (float) contentList.stream().mapToDouble(BaseRich::width).sum();

		if (BaseRich.Align.LEFT.equals(align)) {
			return 0;
		} else if (BaseRich.Align.CENTER.equals(align)) {
			return (width - actualWidth) / 2;
		} else if (BaseRich.Align.RIGHT.equals(align)) {
			return (width - actualWidth);
		} else {
			return 0;
		}
	}

	private float y(List<BaseRich> contentList) {
		return (float) contentList.stream().mapToDouble(BaseRich::height).max().getAsDouble();
	}
}
